package jwebtester.dal.provider.impl;

import infrastructure.Scopes;

import java.util.Date;
import java.util.List;

import jwebtester.business.services.MapperService;
import jwebtester.dal.entities.AuthenticationEntity;
import jwebtester.dal.entities.AuthenticationHistoryEntity;
import jwebtester.dal.entities.PermissionEntity;
import jwebtester.dal.entities.UserEntity;
import jwebtester.dal.provider.SecurityDataProvider;

import org.hibernate.JDBCException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import utils.CollectionUtils;
import domain.entities.Authentication;
import domain.entities.Permission;
import domain.entities.User;

@Repository
@Scope(value = Scopes.Request, proxyMode = ScopedProxyMode.INTERFACES)
public class SecurityDataProviderImpl implements SecurityDataProvider {

	private static final Logger LOG = LoggerFactory.getLogger(SecurityDataProviderImpl.class);

	@Autowired
	private transient SessionFactory sessionFactory;

	@Autowired
	private transient MapperService mapperService;

	@Override
	@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
	public Authentication getAuthentication(final String login) {
		final Session session = sessionFactory.getCurrentSession();

		try {
			Authentication result = null;

			final Query query = session
					.createQuery("from AuthenticationEntity a where a.login = :login");
			query.setString("login", login);
			query.setMaxResults(1);

			final AuthenticationEntity dataEntity = (AuthenticationEntity) query.uniqueResult();
			if (dataEntity != null) {
				final Authentication authentication = (Authentication) mapperService.mapTo(
						dataEntity, Authentication.class);

				result = authentication;
			}

			return result;
		} catch (RuntimeException ex) {
			LOG.error("Error loading authentication", ex);
			throw ex;
		}
	}

	@Override
	@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
	public User getUser(final int userID) {
		final Session session = sessionFactory.getCurrentSession();

		try {
			Query query = session
					.createQuery("from UserEntity u where u.id = :userID and u.isDeleted = false");
			query.setInteger("userID", userID);
			query.setMaxResults(1);

			final UserEntity dataEntity = (UserEntity) query.uniqueResult();
			User user = null;

			if (dataEntity != null) {
				user = (User) mapperService.mapTo(dataEntity, User.class);

				query = session
						.createQuery("select p from PermissionEntity p join p.rolesPermissionEntities rp"
								+ " where rp.roleEntity.id in (select ur.roleEntity.id from UsersRolesEntity ur"
								+ " where ur.userEntity.id = :userID)");
				query.setInteger("userID", userID);

				final List<PermissionEntity> permissions = CollectionUtils.castList(query.list());
				for (PermissionEntity permissionDataEntity : permissions) {
					final Permission permission = (Permission) mapperService.mapTo(
							permissionDataEntity, Permission.class);
					user.addPermission(permission);
				}
			}

			return user;
		} catch (RuntimeException ex) {
			LOG.error(String.format("Error loading user. ID=%s", userID), ex);
			throw ex;
		}
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void saveAuthenticationHistory(final int authID) throws JDBCException {
		final Session session = sessionFactory.getCurrentSession();

		try {
			final AuthenticationHistoryEntity dataEntity = new AuthenticationHistoryEntity();
			dataEntity.setSuccess(true);
			dataEntity.setAuthDate(new Date());

			final AuthenticationEntity auth = new AuthenticationEntity();
			auth.setId(Integer.valueOf(authID));

			dataEntity.setAuthenticationEntity(auth);

			session.persist(dataEntity);
		} catch (JDBCException ex) {
			LOG.error("Error persisting authentication history", ex);
			throw ex;
		}
	}
}
